In this step you implement the drag and drop functionality.
In this section you first use the Kanzi Engine API to instantiate the Drag Item prefab template that you created in the previous step of the tutorial. Then you define the behavior when the user starts the drag-and-drop gesture and drags a button. Finally, you create and configure a drag-and-drop manipulator for each button in the navigation bar.
To create the drag functionality:
onProjectLoaded
() function instantiate the Drag Item prefab that you use to visualize the button that the user is dragging:virtual void onProjectLoaded() KZ_OVERRIDE { ... // Get the reference to the Drag Item prefab. ResourceManager* resourceManager = getDomain()->getResourceManager(); PrefabTemplateSharedPtr dragItemPrefab = resourceManager->acquireResource<PrefabTemplate>("kzb://drag_and_drop/Prefabs/Drag Item"); // Instantiate the Drag Item prefab. m_dragItem = dragItemPrefab->instantiate<Node2D>("Drag Item"); // Get the RootPage node using its alias. Node2DSharedPtr rootPage = screen->lookupNode<Node2D>("#RootPage"); // Add to the RootPage node the instance of the Drag Item prefab that you created. rootPage->addChild(m_dragItem); // Disable the Visible property of the Drag Item prefab instance. // You hide the Drag Item when the user is not dragging it. m_dragItem->setVisible(false); } private: ... // Define a member variable for the instantiated Drag Item. Node2DSharedPtr m_dragItem;
DragAndDrop
class create a function which moves the Drag Item prefab instance:private: ... // Update the position of the Drag Item. void updateDragAndDrop(Vector2 dragPosition, Matrix3x3 dragWorldTransform) { // Calculate the local drag anchor that is the top-left corner of the button that the user is dragging. Vector2 localDragAnchor = dragPosition - m_dragGrabOffset; // Restrict the movement of the Drag Item to the x axis. localDragAnchor.setY(0.0f); // Calculate the global drag anchor. Vector2 globalDragAnchor = dragWorldTransform * localDragAnchor; // The structure that you use to describe the Render Transformation property of the Drag Item. SRTValue2D transform; // Set the Render Transformation property Translation property field to the global drag anchor. transform.setTranslation(globalDragAnchor); // Move the Drag Item for the amount of the dragged distance. m_dragItem->setRenderTransformation(transform); // Set the icons of the buttons. updateItems(); } ... // Define a member variable for the offset from the top-left corner of the button where the user pressed down or clicked it. Vector2 m_dragGrabOffset; };
DragAndDrop
class define the handler for the DragAndDropManipulator::StartedMessage
message:private:
...
// Define the handler for the DragAndDropManipulator::StartedMessage
message from 2D nodes
// that have an input manipulator which generates drag-and-drop messages.
// This prepares the 2D node for dragging.
void onDragStarted(DragAndDropManipulator::StartedMessageArguments& messageArguments)
{
// Get from the message arguments the button that the user starts to drag.
Node2DSharedPtr dragSourceItem = dynamic_pointer_cast<Node2D>(messageArguments.getSource());
// Get the size of the button that the user starts to drag.
Vector2 dragSourceItemSize = dragSourceItem->getActualSize();
// Set the size of the Drag Item to be the same as the size of that button.
m_dragItem->setSize(dragSourceItemSize.getX(), dragSourceItemSize.getY());
// Move the Drag Item to the correct position.
updateDragAndDrop(messageArguments.getPoint(), dragSourceItem->getWorldTransform());
// Make the Drag Item visible.
m_dragItem->setVisible(true);
}
...
onDragStarted
function define the handler for the DragAndDropManipulator::MovedMessage
message: // Define the handler for the DragAndDropManipulator::MovedMessage
message from 2D nodes
// that have an input manipulator which generates drag-and-drop messages.
void onDragMoved(DragAndDropManipulator::MovedMessageArguments& messageArguments)
{
// Get from the message arguments the button that the user is dragging.
Node2DSharedPtr dragSourceItem = dynamic_pointer_cast<Node2D>(messageArguments.getSource());
// Move the Drag Item and update the icons of the buttons.
updateDragAndDrop(messageArguments.getPoint(), dragSourceItem->getWorldTransform());
}
DragAndDrop
class add a function that creates and configures a drag-and-drop manipulator for a node:// Create and configure a drag-and-drop manipulator for a node. void createDragAndDropManipulator(NodeSharedPtr dragSourceItem) { Domain* domain = getDomain(); // Create an input manipulator that generates drag-and-drop messages. DragAndDropManipulatorSharedPtr dragAndDropManipulator = DragAndDropManipulator::create(domain); // Add the input manipulator to the node. dragSourceItem->addInputManipulator(dragAndDropManipulator); // Set the duration of the long press before the drag-and-drop starts to 200 ms. The default is 500 ms. // This is the amount of time the user must press the node before they can start dragging it. dragAndDropManipulator->setPressDuration(chrono::milliseconds(200)); // Subscribe to theDragAndDropManipulator::StartedMessage
message at the node. // TheDragAndDropManipulator
generates this message when the user presses the node // for the duration set by DragAndDropManipulator::setPressDuration. dragSourceItem->addMessageHandler(DragAndDropManipulator::StartedMessage, bind(&DragAndDrop::onDragStarted, this, placeholders::_1)); // Subscribe to theDragAndDropManipulator::MovedMessage
message at the node. // TheDragAndDropManipulator
generates this message when the pointer moves. dragSourceItem->addMessageHandler(DragAndDropManipulator::MovedMessage, bind(&DragAndDrop::onDragMoved, this, placeholders::_1)); }
onProjectLoaded()
function call the createDragAndDropManipulator
function for each button:virtual void onProjectLoaded() KZ_OVERRIDE { ... // Create a drag-and-drop manipulator for each button. // You get the button nodes using their aliases. createDragAndDropManipulator(screen->lookupNode<Node>("#Navigation")); createDragAndDropManipulator(screen->lookupNode<Node>("#Phone")); createDragAndDropManipulator(screen->lookupNode<Node>("#Applications")); createDragAndDropManipulator(screen->lookupNode<Node>("#Music")); createDragAndDropManipulator(screen->lookupNode<Node>("#Car")); }
In this section you set the icon and position of the button that the user drags and reposition the icons of the buttons as the user drags one of the buttons.
To complete the drag functionality:
onDragStarted
function before you call the updateDragAndDrop
function set the icon for the Drag Item and correctly position the node:void onDragStarted(DragAndDropManipulator::StartedMessageArguments& messageArguments) { ... // Get the data context object of the button that the user starts to drag. m_draggedDataContext = dynamic_pointer_cast<DataObject>(dragSourceItem->getProperty(DataContext::DataContextProperty)); // Set the Data Context property of the Drag Item to the data context of the button. // This way you set the Drag Item to have the same icon as the button that the user starts to drag. m_dragItem->setProperty(DataContext::DataContextProperty, m_draggedDataContext); // Save the point from which the user started dragging the node, // relative to the origin (by default the top-left corner) of the node. m_dragGrabOffset = messageArguments.getPoint(); ... } ... // Define a member variable for the data context object of the button that the user drags. DataObjectSharedPtr m_draggedDataContext; };
updateDragAndDrop
function, before you call the updateItems()
function, add the code that repositions the icons of all buttons as the user drags one of the buttons:void updateDragAndDrop(Vector2 dragPosition, Matrix3x3 dragWorldTransform) { ... // Get the global pointer position. Vector2 globalPointerPosition = dragWorldTransform * dragPosition; // Convert global coordinates to local coordinates of the Grid Layout 2D node. Vector2 hitTestPoint = *m_grid->globalToLocal(globalPointerPosition); // Get the width of the button. float cellWidth = m_grid->getActualColumnSize(0); // Calculate the index of the button in the Grid Layout 2D node. unsigned int cellIndex = 0; if (hitTestPoint.getX() > 0.0f) { cellIndex = static_cast<unsigned int>(hitTestPoint.getX() / cellWidth); cellIndex = min(cellIndex, m_grid->getChildCount() - 1u); } // Remove the data object from the old position. m_rootData->removeChild(*m_draggedDataContext); // Insert the data object to the new position. m_rootData->insertChild(cellIndex, m_draggedDataContext); ... }
updateItems()
function in the for
loop add the if-else
clause which hides the icon of the button while the user drags it: void updateItems()
{
...
for (; dataIt != endDataIt; dataIt++, nodeIt++)
{
...
// If the button node is the one that the user is dragging, hide it.
// You hide the node because you use the Drag Item to visualize the dragging of the node.
if (m_draggedDataContext && itemData == m_draggedDataContext)
{
itemNode->setVisible(false);
}
else
{
itemNode->setVisible(true);
}
}
}
In the previous sections you implemented the dragging of a button. When the user ends the drag-and-drop gesture, the Drag Item stays visible in the exact position where the user releases the pointer. In this section you add the code to make the button look like it falls into its place when the user drops it.
To create the drop functionality:
onDragMoved
function define the handler for the DragAndDropManipulator::FinishedMessage
message: // Define the handler for the DragAndDropManipulator::FinishedMessage
message from 2D nodes
// that have an input manipulator which generates drag-and-drop messages.
void onDragFinished(DragAndDropManipulator::FinishedMessageArguments&)
{
// Hide the Drag Item prefab instance.
m_dragItem->setVisible(false);
// Clear the pointer to the data context object.
m_draggedDataContext.reset();
// Assign the correct icons to the buttons.
updateItems();
}
createDragAndDropManipulator
function subscribe to the DragAndDropManipulator::FinishedMessage
message at the node:// Create and configure a drag-and-drop manipulator for a node. void createDragAndDropManipulator(NodeSharedPtr dragAndDropNode) { ... // Subscribe to theDragAndDropManipulator::FinishedMessage
message at the node. // TheDragAndDropManipulator
generates this message when the user ends the drag-and-drop gesture // by releasing the pointer. dragSourceItem->addMessageHandler(DragAndDropManipulator::FinishedMessage, bind(&DragAndDrop::onDragFinished, this, placeholders::_1)); }
Build and run your application.
When you end the drag-and-drop gesture, the Drag Item becomes invisible.
To learn more about the drag-and-drop input manipulator, see Using the drag-and-drop manipulator.
To learn more about handling user input in Kanzi, see Handling user input.